package com.fuang.io.nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;

/**
 * nio客户端：
 * 1，新建Selector
 * 2，新建SocketChannel
 * 3，发起连接请求，并注册监听连接就绪事件
 * 4，当Selector返回连接就绪，则注册监听读就绪事件，并向服务端发送请求数据
 * 5，当Selector返回读就绪事件，处理服务端返回数据
 * Created by Fuang on 2018-9-19.
 */
public class TimeClientHandle implements Runnable {

    private String host;
    private int port;
    private Selector selector;
    private SocketChannel socketChannel;
    private volatile boolean stop;

    public TimeClientHandle(String host, int port) {
        this.host = host;
        this.port = port;
        try {
            selector = Selector.open();
            socketChannel = SocketChannel.open();
            socketChannel.configureBlocking(false);
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    @Override
    public void run() {
        try {
            doConnect();
        } catch (IOException e) {
            e.printStackTrace();
        }

        while (!stop) {
            try {
                selector.select(1000);
                Set<SelectionKey> selectionKeys = selector.selectedKeys();
                Iterator<SelectionKey> it = selectionKeys.iterator();
                while (it.hasNext()) {
                    SelectionKey key = it.next();

                    handleInput(key); // 处理就绪事件：连接就绪、读就绪

                    it.remove();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        };
    }

    private void handleInput(SelectionKey key) throws IOException {
        if (!key.isValid()) {
            return;
        }

        SocketChannel socketChannel = (SocketChannel) key.channel();
        if (key.isConnectable()) { // 连接就绪事件处理
            if (socketChannel.finishConnect()) { // 连接成功
                socketChannel.register(selector, SelectionKey.OP_READ); // 监听读就绪
                write(socketChannel); // 发送request
            } else {
                System.exit(1); // 退出进程
            }
        }

        if (key.isReadable()) { // 读就绪处理
            read(socketChannel);

            key.cancel(); // 关闭资源
            socketChannel.close(); // 关闭资源
        }
    }

    /**
     * 请求连接
     * @throws IOException
     */
    private void doConnect() throws IOException {
        if (socketChannel.connect(new InetSocketAddress(host, port))) { // 连接成功
            socketChannel.register(selector, SelectionKey.OP_READ); // 监听读就绪
            write(socketChannel); // 发送request
        } else { // 连接失败，监听连接就绪
            socketChannel.register(selector, SelectionKey.OP_CONNECT);
        }
    }

    private void read(SocketChannel socketChannel) throws IOException {
        ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
        int bytesRead = socketChannel.read(byteBuffer); // 从socketChannel读数据到buffer
        StringBuilder body = new StringBuilder();
        while (bytesRead != -1) {
            byteBuffer.flip(); // buffer从写状态翻转到读状态
            byte[] bytes = new byte[byteBuffer.remaining()];
            byteBuffer.get(bytes);
            body.append( new String(bytes, "UTF-8"));
            byteBuffer.clear();
            bytesRead = socketChannel.read(byteBuffer);
        }
        System.out.println("Now is : " + body);
    }

    private void write(SocketChannel socketChannel) throws IOException {
        byte[] req = "QUERY TIME ORDER".getBytes();
        ByteBuffer byteBuffer = ByteBuffer.allocate(req.length);
        byteBuffer.put(req);
        byteBuffer.flip();
        socketChannel.write(byteBuffer);
        System.out.println("Send order 2 server succeed.");
    }
}
